//
//  patchcgfile.m
//  patchcg
//
//  this doesn't work exactly as advertised, but it should be enough to get
//  you going.
//
//  Created by thrust on 25/01/06.
//  Copyright 2006 __MyCompanyName__. All rights reserved.
//

#import "patchcgfile.h"
#import "NSMachOFileImage.h"
#import "NSMachOHeader.h"
#import <mach-o/loader.h>

int
fakeCGXLFindBestPositionForDisplay() {
	return 1;
}

void marker() {}

int
patchCGFile() {
	NSLog(@"patchCGFile");
	
	// locate coregraphics bundle
	NSLog(@"locating coregraphics bundle");
	NSBundle *cgBundle = [NSBundle bundleWithIdentifier:@"com.apple.CoreGraphics"];
	
	if (cgBundle == nil) {
		// not found, so bail
		NSLog(@"could not locate coregraphics bundle");
		return -1;
	}
	
	// do we care if cg is loaded? we're just a foundation app,
	// so it might not be. does this affect the rest of the NSBundle calls?
	if (![cgBundle isLoaded]) {
		// coregraphics isn't loaded, so load it?
		NSLog(@"coregraphics not loaded");
		//[cgBundle load];
	}
	
	// coregraphics framework found. query version & path. note that cg uses
	// a messed up versioning scheme, so don't expect to get it as a number
	NSString *cgVersion = [cgBundle objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
	NSString *cgPath = [cgBundle executablePath];
	NSLog(@"coregraphics v%@ @ %@", cgVersion, cgPath);

	// attach to the image with vmutils
	// limit to powerpc right now, I don't know how to patch x86 yet.
	// 10.4.4 for intel includes a universal, safety check with it.
	NSLog(@"attaching vmutils image to file");
	NSMachOFileImage *theFileImage = nil;
	theFileImage = [[NSMachOFileImage alloc]
			initWithPath: cgPath
			 withCPUType: CPU_TYPE_POWERPC];
	if (theFileImage == nil) {
		// error attaching to image
		NSLog(@"error attaching to file");
		return -1;
	}
	NSLog(@"coregraphics successfully imaged");
	
	// enumerate headers
	
	NSLog(@"searching image for CGXLFindBestPositionForDisplay");
	unsigned long symAddr = 0;
	symAddr = [theFileImage addressOfSymbolWithCName:"CGXLFindBestPositionForDisplay"];
	if (symAddr == 0) {
		// couldn't find symbol, wrong version?
		NSLog(@"could not locate CGXLFindBestPositionForDisplay");
		return -1;
	}
	NSLog(@"CGXLFindBestPositionForDisplay @ 0x%08x in vm", symAddr);
	
	// how do we push this symbol back to a file address?
	// we need to know what image it's in, ppc or x86
	
	// get header from image
	NSArray *theHeaderArray;
	theHeaderArray = [theFileImage getMachHeaders];
	NSLog(@"there are %d mach-o headers in the file image", [theHeaderArray count]);
	
	// if not one header, then we have a problem..
	if ([theHeaderArray count] != 1) {
		NSLog(@"too [many,few] headers found in image");
		return -1;
	}
	NSMachOHeader *theHeader = [theHeaderArray objectAtIndex:0];
	NSLog(@"header slide value: 0x%08x", [theHeader slideValue]);
	
	// what we're looking for is really at 0x00022fe8 in the file
	// but at 0x90393fe8 in vm. how do we map backwards?
	//
	
	struct segment_command *theSegCommand = [theHeader loadCommandForSegmentNamed:@"__TEXT"];
	
	NSLog(@"segment: %s", theSegCommand->segname);
	NSLog(@"fileoffset: 0x%08x", theSegCommand->fileoff);
	NSLog(@"vmaddr: 0x%08x", theSegCommand->vmaddr);
	
	// what do we really want to do with the slide value and file offset? add or subtract or?
	// doublecheck this!
	UInt32 fileAddr = symAddr - [theHeader slideValue] - theSegCommand->fileoff - theSegCommand->vmaddr;
	NSLog(@"CGXLFindBestPositionForDisplay @ 0x%08x in file", fileAddr);
	
	NSMutableData *cgData = [NSMutableData dataWithContentsOfFile:cgPath];
	
	UInt32 op;
	NSRange opLocn;
	opLocn.location = fileAddr;
	opLocn.length = 4;
	
	int idx;
	for (idx = 0; idx < 4; idx++) {
		opLocn.location += idx * 4;
		[cgData getBytes: &op range: opLocn];
		NSLog(@"op: %08x", op);
	}
	
	// on ppc copy 8 bytes from fakeCGXLFindBestPositionForDisplay, on
	// intel copy 10. probably want to open ourselves with vmutils so we can
	// copy the architecture we're not running on.
	
	printf("\n");
	
	return 0;
}
